﻿using System.Threading.Tasks;
using Microsoft.AspNet.SignalR;
using System.Collections.Generic;
using System.Linq;
using verk2013_project_group12.Filters;
using verk2013_project_group12.Models;
using WebMatrix.WebData;
using System.Web;
using System;
using verk2013_project_group12.Code;

namespace verk2013_project_group12.Controllers
{
    //	[Authorize]
    public class GameHub : Hub
    {

        [InitializeSimpleMembership]
        public void Join(string groupId)
        {
            /* This function does a couple of things. Firstly it adds the players to a group which id
            corresponds with the id of the game instance they are playing.
            It will also take care of initializing the game interface appropriately.
            */
            var theInstanceId = -1;
            Int32.TryParse(groupId, out theInstanceId);
            // First we get the id of the instance which we will use as the groupId

            UserRepository users = new UserRepository();
            var username = HttpContext.Current.User.Identity.Name;
            var userId = users.GetUserByUsername(username).UserId;
            // Get the id of the user trying to join the group

            GameInstanceRepository instances = new GameInstanceRepository();
            var theInstance = instances.GetInstanceById(theInstanceId);
            if (theInstance == null)
            {
                return;
                /* Do nothing if the instance does not exist (this should almost never happen
                because trying to join an invalid instance directs to the error view. */
            }

            GameRegistrationRepository regs = new GameRegistrationRepository();
            var totalRegistrations = regs.GetRegistrationsByInstId(theInstanceId);
            // Get all the registrations for the instance

            var totalRegistrationsCount = totalRegistrations.Count();
            // Get the count of all registrations for the instance

            if (totalRegistrationsCount < theInstance.PlayerCap)
            {
                // Case: The game instance has a free slot
                var theRegistration = (from r in regs.GetRegistrationsByInstId(theInstanceId)
                                      where r.PlayerUserId == userId
                                      select r).SingleOrDefault();

                if (theRegistration != null)
                {
                    if (theInstance.GameID == 1)
                    {
                        // Case: Tic-Tac-Toe
                        Clients.Caller.eraseCells();
                        return;
                        // In this case the user is already registered
                    }
                    if (theInstance.GameID == 2)
                    {
                        // Case: Yahtzee
                        Clients.Caller.hideYahtzee();
                        return;
                        // In this case the user is already registered
                    }
                }
                GameRegistration reg = new GameRegistration();
                reg.InstanceID = theInstanceId;
                reg.PlayerUserId = userId;
                regs.Add(reg);
                regs.Save();
                theInstance.CurrentPlayers++;
                // Register the player for the instance

                if (theInstance.CurrentPlayers == theInstance.PlayerCap)
                {
                    // Case: Player cap for the instance is reached
                    theInstance.Running = true;
                    if (theInstance.GameID == 1)
                    {
                        // Case: Tic-Tac-Toe
                        Clients.OthersInGroup(groupId).showCells(username);
                        // "Start the game" by showing the game interface to the opponent as well as who he's playing against

                        var otherUserId = (from r in regs.GetRegistrationsByInstId(theInstanceId)
                                           where r.PlayerUserId != userId
                                           select r.PlayerUserId).SingleOrDefault();
                        var otherUserName = users.GetUserById(otherUserId).UserName;
                        // Get the username of the opponent

                        Clients.Caller.showCells(otherUserName);
                        // For the caller we do the same

                    }
                    if (theInstance.GameID == 2)
                    {
                        // Case: Yahtzee
                        List<string> players = new List<string>();
                        foreach (var r in totalRegistrations)
                        {
                            players.Add(users.GetUserById(r.PlayerUserId).UserName);
                        }
                        string [] playerNames = players.ToArray();
                        // Store all the players registered in an array

                        Clients.All.addScorecard(playerNames);
                        // Display the scorecard with the usernames for all players
                    }
                }
                int playerNumber = 1;
                if (theInstance.GameID == 1)
                {
                    // Case: Tic-Tac-Toe
                    foreach (var r in totalRegistrations)
                    {
                        if (r.PlayerUserId == userId)
                        {
                            Clients.Caller.initPlayerTurn(playerNumber);
                            // Initialize who's turn it is depending on the order of registrations
                            break;
                        }
                        playerNumber++;
                    }
                }
                else if (theInstance.GameID == 2)
                {
                    //Case: Yahtzee
                    foreach (var r in totalRegistrations)
                    {
                        if (r.PlayerUserId == userId)
                        {
                            Clients.Caller.initPlayerId(playerNumber);
                            // Assign a player number to each player in yahtzee
                            break;
                        }
                        playerNumber++;
                    }
                }
                instances.Save();
                Groups.Add(Context.ConnectionId, groupId);
                // Register the player and add him to the group if the player cap has not yet been reached
                return;
            }
            if (theInstance.GameID == 1)
            {
                // Case: Tic-Tac-Toe
                Clients.Caller.eraseCells();
                // If this point is reached the user should not be able to see the board (tic-tac-toe)
            }
            if (theInstance.GameID == 2)
            {
                // Case: Yahtzee
                Clients.Caller.hideYahtzee();
                // If this point is reached the user should not be able to see the interface (yahtzee)
            }
        }

        // Chat
        public void Send(string groupId, string message)
        {
            var username = HttpContext.Current.User.Identity.Name;
            Clients.OthersInGroup(groupId).broadcastMessage(username, message);
            Clients.Caller.broadcastMessage(username, message);
            // Displays the message along with the user associated to everyone in the group
        }

        // Tic-Tac-Toe
        public void ClickCell(string groupName, string cellId)
        {
            // This is a function executed every time a player makes a valid move
            Clients.OthersInGroup(groupName).cellClicked(cellId);
        }

        public void TicTacToeWin(string groupId)
        {
            // The winning player calls this function

            var theInstanceId = -1;
            Int32.TryParse(groupId, out theInstanceId);
            // Get the id of the game instance

            UserRepository users = new UserRepository();
            var username = HttpContext.Current.User.Identity.Name;
            var user = users.GetUserByUsername(username);
            // Get the winner of the game

            GameRegistrationRepository regs = new GameRegistrationRepository();
            var otherUserId = (from r in regs.GetRegistrationsByInstId(theInstanceId)
                              where r.PlayerUserId != user.UserId
                              select r.PlayerUserId).SingleOrDefault();

            var theOtherUser = users.GetUserById(otherUserId);
            // Get the opponent

            List<UserProfile> players = new List<UserProfile> { user, theOtherUser };
            Scoring scoring = new Scoring(user, players);
            scoring.GiveScore();
            users.Save();
            // Subtract points from the losers score and add points to the winners score
        }

        public void WinningIds(string id1, string id2, string id3)
        {
            Clients.All.showWinningCells(id1, id2, id3);
            // Make the 3 cells that form a win stand out to the players
        }

        public void TicTacToeQuit(string groupId)
        {
            // This function is called when a player disconnects before the game is over

            Clients.OthersInGroup(groupId).concede();
            // Inform the opponent that the player has conceded

            var theInstanceId = -1;
            Int32.TryParse(groupId, out theInstanceId);
            // Get the instance id

            UserRepository users = new UserRepository();
            var username = HttpContext.Current.User.Identity.Name;
            var user = users.GetUserByUsername(username);

            GameRegistrationRepository regs = new GameRegistrationRepository();
            var otherUserId = (from r in regs.GetRegistrationsByInstId(theInstanceId)
                               where r.PlayerUserId != user.UserId
                               select r.PlayerUserId).SingleOrDefault();

            var theOtherUser = users.GetUserById(otherUserId);
            // Get the opponent

            if (theOtherUser != null)
            {
                List<UserProfile> players = new List<UserProfile> { user, theOtherUser };
                Scoring scoring = new Scoring(theOtherUser, players);
                scoring.GiveScore();
                users.Save();
            }
            // Subtract points from the losers score and add points to the winners score

            GameInstanceRepository instances = new GameInstanceRepository();
            var theInstance = instances.GetInstanceById(theInstanceId);
            instances.Remove(theInstance);
            instances.Save();
            var theRegs = from r in regs.GetRegistrationsByInstId(theInstanceId)
                          select r;
            foreach (var reg in theRegs)
            {
                regs.Remove(reg);
            }
            regs.Save();
            // Remove the instance and corresponding registrations from the database
        }

        public void TicTacToeEnd(string groupId)
        {
            var theInstanceId = -1;
            Int32.TryParse(groupId, out theInstanceId);

            GameInstanceRepository instances = new GameInstanceRepository();
            var theInstance = instances.GetInstanceById(theInstanceId);
            instances.Remove(theInstance);
            instances.Save();

            GameRegistrationRepository regs = new GameRegistrationRepository();
            var theRegs = from r in regs.GetRegistrationsByInstId(theInstanceId)
                          select r;
            foreach (var reg in theRegs)
            {
                regs.Remove(reg);
            }
            regs.Save();
            // remove the instance and corresponding registrations from the database
        }

        // Yahtzee

        public void BroadcastScore(string groupId, string scoreId, string totalScore, string rollScore)
        {
            Clients.OthersInGroup(groupId).showScore(scoreId, totalScore, rollScore);
            // Inform the other players what the user rolled and what the current total and roll scores are
        }

        public void HandleQuit(string groupId, string leaderId)
        {
            // leaderId tells us where in the group of registered players the leading player is
            var theInstanceId = -1;
            Int32.TryParse(groupId, out theInstanceId);
            
            var theNumber = -1;
            Int32.TryParse(leaderId, out theNumber);

            int count = 1;

            int theLeaderId = 0;

            GameRegistrationRepository regs = new GameRegistrationRepository();
            var totalRegistrations = regs.GetRegistrationsByInstId(theInstanceId);
            foreach (var reg in totalRegistrations)
            {
                if (theNumber == count)
                {
                    theLeaderId = reg.PlayerUserId;
                    break;
                }
                count++;
            }
            // Get the UserId of the leader

            GameInstanceRepository instances = new GameInstanceRepository();
            var theInstance = instances.GetInstanceById(theInstanceId);
            if (theLeaderId != 0)
            {
                theInstance.LeaderUserId = theLeaderId;
                // Add the leaderId to the instance
            }
            theInstance.CurrentPlayers--;
            // Drop the current player count
            instances.Save();

            if (theInstance.CurrentPlayers == 0)
            {
                YahtzeeEnd(theInstance, totalRegistrations);
                // This function is called when the game instance has ended
            }

        }

        public void YahtzeeEnd(GameInstance theInstance, IEnumerable<GameRegistration> totalRegistrations)
        {
            List<int> playerIds = new List<int>();
            foreach (var reg in totalRegistrations)
            {
                playerIds.Add(reg.PlayerUserId);
            }
            
            List<UserProfile> players = new List<UserProfile>();
            UserRepository users = new UserRepository();
            foreach (var id in playerIds)
            {
                players.Add(users.GetUserById(id));
            }
            // Get all the players registered for the game instance

            var theLeader = users.GetUserById(theInstance.LeaderUserId);
            Scoring scoring = new Scoring(theLeader, players);
            scoring.GiveScore();
            users.Save();
            // Award points to the winner and subtract points from the rest

            GameRegistrationRepository regs = new GameRegistrationRepository();
            foreach (var reg in totalRegistrations)
            {
                regs.Remove(reg);
            }
            regs.Save();
            // Remove the registrations

            GameInstanceRepository instances = new GameInstanceRepository();
            instances.Remove(theInstance);
            instances.Save();
            // Delete the instance
        }

        public void BroadcastBonus(string groupId, string playerId, string bonus)
        {
            Clients.OthersInGroup(groupId).showBonus(playerId, bonus);
            // Show bonus points to other players
        }

        public void GameOver(string leaderId)
        {
            // Every player will call this function

            Clients.Caller.showWin(leaderId);
            // Inform the players if they have won the game or not
        }
    }
}
